package com.lenovo.lcdm.type.util;

import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.lenovo.lcdm.common.enums.CommonMsgEnum;
import com.lenovo.lcdm.common.exception.BusinessException;
import com.lenovo.lcdm.type.dto.AttributeGroupDTO;
import com.lenovo.lcdm.type.dto.TypeDefinitionDTO;
import com.lenovo.lcdm.type.dto.VerifyLocalAttrInfoDTO;
import com.lenovo.lcdm.type.enums.DataTypeEnum;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
 * @author huanghr2
 */
public class LocalAttributeUtil {

    private static Logger log = LoggerFactory.getLogger(LocalAttributeUtil.class);


    public static void verifyAddLocalAttribute(TypeDefinitionDTO typeDetail,
                                               Long globalAttributeId,
                                               String dataType,
                                               Long groupId,
                                               String referencedAttribute,
                                               String referencedValueProvider) {
        if (ObjectUtil.isAnyEmpty(typeDetail, globalAttributeId, dataType)) {
            return;
        }

        Map<Long, List<VerifyLocalAttrInfoDTO>> currentVerifyInfo =
                convert(typeDetail, TypeDefinitionDTO::getId, AttributeGroupDTO::getGroupId);
        if (MapUtils.isEmpty(currentVerifyInfo)) {
            return;
        }

        currentVerifyInfo.computeIfAbsent(groupId, k -> Lists.newLinkedList())
                .add(new VerifyLocalAttrInfoDTO(globalAttributeId, dataType, referencedAttribute, referencedValueProvider));
        verifyLocalAttribute(currentVerifyInfo, typeDetail.getInternalName());
    }

    public static void verifyUpdateRefLocalAttribute(TypeDefinitionDTO typeDetail,
                                               Long localAttrId,
                                               Long globalAttributeId,
                                               String dataType,
                                               String referencedAttribute,
                                               String referencedValueProvider) {
        if (!DataTypeEnum.NAME_REFERENCE.getType().equalsIgnoreCase(dataType)) {
            return;
        }

        if (ObjectUtil.isAnyEmpty(typeDetail, typeDetail.getLocalAttributes(), globalAttributeId, dataType)) {
            return;
        }

        // 校验更新后的属性值和其他属性值相等即可
//        typeDetail.getLocalAttributes().stream()
//                .filter(localAttr -> Objects.equals(globalAttributeId, localAttr.getGlobalAttributeId()))
//                .filter(localAttr -> !Objects.equals(localAttrId, localAttr.getId()))
//                .findFirst()
//                .ifPresent(localAttr -> {
//                    Checker.equals(
//                            Strings.nullToEmpty(referencedAttribute),
//                            Strings.nullToEmpty(localAttr.getReferencedAttribute()),
//                            "The referenced attribute of the same global attribute in same type must be equal");
//
//                    Checker.equals(
//                            Strings.nullToEmpty(referencedValueProvider),
//                            Strings.nullToEmpty(localAttr.getReferencedValueProvider()),
//                            "The referenced value provider of the same global attribute in same type must be equal");
//                });
    }

    private static <T, G> Map<G, List<VerifyLocalAttrInfoDTO>> convert(TypeDefinitionDTO typeDetail,
                                                                       Function<TypeDefinitionDTO, T> typeMapper,
                                                            Function<AttributeGroupDTO, G> groupMapper) {
        Map<G, List<VerifyLocalAttrInfoDTO>> result = Maps.newHashMap();
        if (ObjectUtil.isAnyEmpty(typeDetail, typeDetail.getAttributeGroups())) {
            return null;
        }

        typeDetail.getAttributeGroups().forEach(group -> {
            if (CollectionUtils.isEmpty(group.getLocalAttributes())) {
                return;
            }

            G mappedGroup = groupMapper.apply(group);
            List<VerifyLocalAttrInfoDTO> verifyDTOs = Lists.newArrayListWithCapacity(group.getLocalAttributes().size());
            result.put(mappedGroup, verifyDTOs);
            group.getLocalAttributes().forEach(localAttr -> verifyDTOs.add(new VerifyLocalAttrInfoDTO(
                    localAttr.getGlobalAttributeId(),
                    localAttr.getDataTypeName(),
                    localAttr.getReferencedAttribute(),
                    localAttr.getReferencedValueProvider())));
        });
        return result;
    }

    /**
     * T  Type id 或者Type name
     */
    public static <T, G> void verifyLocalAttribute(Map<G, List<VerifyLocalAttrInfoDTO>> groupMapByType,
                                                   T type) {
        if (ObjectUtil.isAnyEmpty(groupMapByType)) {
            return;
        }

        // 类型下，ref类型之外的全局属性id
        Set<Long> globalAttrIdsWithoutRefByType = Sets.newHashSet();
        // 类型下，ref类型属性的值 key： global attr id
        Map<Long, Pair<String, String>> refGlobalAttr2ValueByType = Maps.newHashMap();
        // 分组中，已经包含的全局属性id
        Map<G, Set<Long>> group2GlobalAttrIds = Maps.newHashMap();

        if (MapUtils.isNotEmpty(groupMapByType)) {
            groupMapByType.forEach((group, localAttrs) -> {
                if (!CollectionUtils.isEmpty(localAttrs)) {
                    localAttrs.forEach(localAttr -> {
                        // 分组内属性唯一
                        if (!group2GlobalAttrIds
                                .computeIfAbsent(group, k -> Sets.newHashSet())
                                .add(localAttr.getGlobalAttrId())) {
                            log.info("Attribute already exist in this group, groupMapByType:{}, type:{}, group:{}, localAttr:{}",
                                    JSON.toJSONString(groupMapByType), type, group, JSON.toJSONString(localAttr));
                            throw new BusinessException("Attribute already exist in this group!", CommonMsgEnum.FAIL.getCode());
                        }

                        // 类型下非ref类型全局属性唯一
                        if (!DataTypeEnum.NAME_REFERENCE.name().equalsIgnoreCase(localAttr.getDataType())
                                && !globalAttrIdsWithoutRefByType.add(localAttr.getGlobalAttrId())) {
                            log.info("Attribute already exist in this type, groupMapByType:{}, type:{}, group:{}, localAttr:{}",
                                    JSON.toJSONString(groupMapByType), type, group, JSON.toJSONString(localAttr));
                            throw new BusinessException("Attribute already exist in this type!", CommonMsgEnum.FAIL.getCode());
                        } else {
//                            if (refGlobalAttr2ValueByType.containsKey(localAttr.getGlobalAttrId())) {
//                                Pair<String, String> refAttrValuePair = refGlobalAttr2ValueByType.get(localAttr.getGlobalAttrId());
//                                // 重复的ref属性值必须一样
//                                if (!Objects.equals(Strings.nullToEmpty(refAttrValuePair.getLeft()),
//                                        Strings.nullToEmpty(localAttr.getReferencedAttribute()))) {
//                                    log.info("referenced attribute not equal in this type, groupMapByType:{}, type:{}, group:{}, localAttr:{}",
//                                            JSON.toJSONString(groupMapByType), type, group, localAttr);
//                                    throw new BusinessException("The referenced attribute of the same global attribute in same type must be equal");
//                                }
//
//                                if (!Objects.equals(Strings.nullToEmpty(refAttrValuePair.getRight()),
//                                        Strings.nullToEmpty(localAttr.getReferencedValueProvider()))) {
//                                    log.info("referenced value provider not equal in this type, groupMapByType:{}, type:{}, group:{}, localAttr:{}",
//                                            JSON.toJSONString(groupMapByType), type, group, localAttr);
//                                    throw new BusinessException("The referenced value provider of the same global attribute in same type must be equal");
//                                }
//                            } else {
//                                refGlobalAttr2ValueByType.put(
//                                        localAttr.getGlobalAttrId(),
//                                        Pair.of(localAttr.getReferencedAttribute(), localAttr.getReferencedValueProvider()));
//                            }
                        }
                    });
                }
            });
        }
    }
}
